using System;
using System.Collections.Generic;
using System.Text;
using OpenTK.Math;
using Dogfight2008.Common;
using System.Media;
using OpenTK.OpenGL;
using System.Drawing;

namespace Dogfight2008
{
  public class Plane : PlaneControl, IActive, ICollideable, IRenderable
  {
    RotatedBody2d body = new RotatedBody2d();
    private Countdown reloadedCountdown = null;
    public Mesh Mesh;

    public Plane(Vector2 position, double angle, double speed)
    {
      reloadedCountdown = new Countdown(1);
      reloadedCountdown.Tick(1);
      body = new RotatedBody2d();
      body.Position = position;
      body.Angle = angle;
      body.Velocity = Calc.Polar(angle, speed);
    }

    private double Rotation
    {
      get
      {
        return body.Angle;
      }
      set
      {
        body.Angle = value;
      }
    }


    Vector2 EngineAcc(double rot)
    {
      float engineAcc = 0.5f;
      float sin = (float)Math.Sin(rot);
      float ax = (float)(Math.Cos(rot) / 100.0);
      float ay = sin / 100.0f;
      Vector2 a = new Vector2(ax, ay);
      return engineAcc * a - a * sin * 0.1f;
    }

    PlaneState state = PlaneState.Flying;
    public PlaneState State
    {
      get
      {
        return state;
      }
    }

    public void StartFalling()
    {
      if (state == PlaneState.Flying)
        body.Velocity.Y = 0;
      state = PlaneState.Falling;
    }

    public void Tick(double deltaMS)
    {
      reloadedCountdown.Tick(deltaMS);

      if (StickPosition == StickPosition.Lift)
        Rotation = (Rotation + rotationSpeed * deltaMS) % (Math.PI * 2);
      if (StickPosition == StickPosition.Dive)
        Rotation = (Rotation + Math.PI * 2 - rotationSpeed * deltaMS) % (Math.PI * 2);

      // Engine if flying
      if (state == PlaneState.Flying)
      {
        float acc = Acceleration(Rotation);
        //Vector2 acc = EngineAcc(Rotation);
        float vel = Velocity();
        vel += 0.1f * acc;
        if (vel < fallAtSpeedBelow)
        {
          SystemSounds.Beep.Play();
          state = PlaneState.Falling;
        }
        else
          //vel += acc.Length;
          //acc.Normalize();
          body.Velocity = vel * new Vector2((float)Math.Cos(Rotation), (float)Math.Sin(Rotation));
      }
      else
      {
        body.Velocity -= Vector2.UnitY * gravity;
        if (body.Velocity.Length > regainFlightAtSpeedAbove && RegainFlightRotation(Rotation))
        {
          SystemSounds.Exclamation.Play();
          state = PlaneState.Flying;
          body.Velocity = body.Velocity.Length * new Vector2((float)Math.Cos(Rotation), (float)Math.Sin(Rotation));
        }
      }

      if (Velocity() > maxvel)
        body.Velocity *= maxvel / body.Velocity.Length;

      // Stall?
      // float stallSpeed = 0.025f;
      //if (body.Velocity.Length < stallSpeed)
      //  state = PlaneState.Falling;

      // Gravity
      // body.Velocity -= Vector2.UnitY * (float)deltaMS * 0.0001f;

      // Regain fly state?
      //if( state == PlaneState.Falling)
      //{
      //  double halfang = 10;
      //  double mindir = 270 - halfang;
      //  double maxdir = 270 + halfang;
      //  mindir *= Calc.Deg2Rad;
      //  maxdir *= Calc.Deg2Rad;
      //  if (mindir <= rotation && rotation <= maxdir && body.Velocity.Length > stallSpeed)
      //    state = PlaneState.Flying;
      //}

      // Air resistance
      //float airResitance = 0.6f;
      //if (state == PlaneState.Falling)
      //  airResitance *= 2;
     // body.Velocity -= airResitance * body.Velocity * body.Velocity.Length;

      body.Tick(deltaMS);
    }

    private float Velocity()
    {
      return body.Velocity.Length;
    }

    private bool RegainFlightRotation(double Rotation)
    {
      float down = Calc.DegreesToRadians(270);
      float delta = Calc.DegreesToRadians(regainDeltaDegrees);
      float min = down-delta;
      float max = down+delta;
      return min <= Rotation && Rotation <= max;
    }

    private float Acceleration(double Rotation)
    {
      float delta = -(float)Math.Sin(Rotation);
      if (delta > 0)
        return delta * delta * diveDeltaCoeff + diveAccum;
      else
        return -delta * delta * climbDeltaCoeff + climbAccum;
    }

    public double X {
      get
      {
        return body.Position.X;
      }
      set
      {
        body.Position.X = (float)value;
      }
    }

    public double Y
    {
      get
      {
        return body.Position.Y;
      }
    }

    public double Angle
    {
      get
      {
        return Rotation;
      }
    }

    public Box2d GetBox()
    {
      return new Box2d(body.Position.X, body.Position.Y, -3.6, 1.5, -.25, .25, Rotation);
    //  return new Box2d(body.Position.X, body.Position.Y, -10, 10, -10, 10, rotation);
    }

    //internal void Reset()
    //{
    //  state = PlaneState.Flying;
    //  body.Position = new Vector2(initPos);
    //  body.Angle = initAngle;
    //  body.Velocity = EngineAcc(initAngle);// initSpeed;
    //  //body.Position = Calc.v(0, 0);
    //  //rotation = 0;
    //  //body.Velocity = Vector2.UnitX * 0.1f;
    //}

    static float rotationSpeed = 0.00735f;
    static float fallAtSpeedBelow = 0.04f;
    static float regainFlightAtSpeedAbove = 0.009f;
    static float maxvel = 0.05f;
    static float regainDeltaDegrees = 10;
    static float gravity = 0.0036f;
    static float climbDeltaCoeff = 0.04f;
    static float climbAccum = 0.03f;
    static float diveDeltaCoeff = 0.03f;
    static float diveAccum = 0.05f;
    public static float ReloadTimeMs = 100;
    public static float MuzzleVelocity = 0.1f;
    public static float HarmlessTime = 25.0f;

    internal static void TweakConfig(TweakConfig config)
    {
      rotationSpeed = (float)config.GetPropertyValue("rotationSpeed", rotationSpeed);
      fallAtSpeedBelow = (float)config.GetPropertyValue("fallAtSpeedBelow", fallAtSpeedBelow);
      regainFlightAtSpeedAbove = (float)config.GetPropertyValue("regainFlightAtSpeedAbove", regainFlightAtSpeedAbove);
      maxvel = (float)config.GetPropertyValue("maxVelocity", maxvel);
      regainDeltaDegrees = (float)config.GetPropertyValue("regainDeltaDegrees", regainDeltaDegrees);
      gravity = (float)config.GetPropertyValue("gravity", gravity);
      climbDeltaCoeff = (float)config.GetPropertyValue("climbDeltaCoeff", climbDeltaCoeff);
      climbAccum = (float)config.GetPropertyValue("climbAccum", climbAccum);
      diveDeltaCoeff = (float)config.GetPropertyValue("diveDeltaCoeff", diveDeltaCoeff);
      diveAccum = (float)config.GetPropertyValue("diveAccum", diveAccum);
      ReloadTimeMs = (float)config.GetPropertyValue("reloadTime", ReloadTimeMs);
      MuzzleVelocity = (float)config.GetPropertyValue("muzzleVel", MuzzleVelocity);
      HarmlessTime = (float)config.GetPropertyValue("harmlessTime", HarmlessTime);
    }

    public Shot Fire()
    {
      if (reloadedCountdown.Stopped)
      {
        reloadedCountdown = new Countdown(ReloadTimeMs);
        SystemSounds.Asterisk.Play();
        return new Shot(this.body.Position, this.body.Angle, Velocity() + MuzzleVelocity, HarmlessTime);
      }
      else
        return null;
    }

    public Box2d GetBox2d()
    {
      return this.GetBox();
    }

    private bool ShadowMode = false;
    public void Render(IRenderer renderer)
    {
      ShadowMode = true;
      RenderInternal(renderer);
      ShadowMode = false;
      RenderInternal(renderer);
    }

    private void RenderInternal(IRenderer renderer)
    {
      Plane plane = this;
      Mesh mesh = Mesh;
      bool shadow = ShadowMode;

      Vector3 pos3d = new Vector3((float)plane.X, -20, (float)plane.Y);
      renderer.PushMatrix();
      if (shadow)
      {
        renderer.PushMatrix();
        renderer.Translate(new Vector3(0, 0, -20f));
        GL.Scale(1, 1, 0);
        GL.Disable(OpenTK.OpenGL.Enums.EnableCap.DepthTest);
      }
      renderer.Translate(pos3d);
      renderer.Rotate(-Vector3.UnitY, plane.Angle);
      renderer.Rotate(Vector3.UnitZ, -Math.PI / 2);
      renderer.Color(Color.Green);
      float mult = 1.0f;
      float amin = mult * -3.6f, amax = mult * 1.5f;
      float bmin = mult * -0.25f, bmax = mult * 0.45f;
      if (!shadow)
        renderer.DrawMesh(mesh);
      else
      {
        renderer.Color(Color.Black);
        renderer.DrawMeshIgnoreColors(mesh);
      }
      if (shadow)
      {
        GL.Enable(OpenTK.OpenGL.Enums.EnableCap.DepthTest);
        renderer.PopMatrix();
      }
      renderer.PopMatrix();
    }
  }
}
